#pragma once

#include "Socket.hpp"
#include <iostream>
#include <string>
#include <pthread.h>
#include <vector>
#include <sstream>
#include <fstream>
#include <unordered_map>


const std::string sep = "\r\n";
const std::string wwwroot = "./wwwroot";
const std::string homepage = "index.html";

class HttpServer;

class ThreadData
{
public:
    ThreadData(int sockfd, HttpServer* s) :sockfd(sockfd), svr(s)
    {
    }
public:
    int sockfd;
    HttpServer* svr;
};

class HttpRequest
{
public:
    HttpRequest() {}

    void Deserialization(std::string& req)
    {
        while (true)
        {
            auto pos = req.find(sep);
            if (pos == std::string::npos) break;
            std::string tmp = req.substr(0, pos);
            if (tmp.empty()) break;
            _lines.push_back(tmp);

            req.erase(0, pos + sep.size());
        }
        _text = req;
    }

    void parse()
    {
        std::stringstream ss(_lines[0]);
        ss >> _method >> _url >> _httpversion;
        _file_path = wwwroot;
        if (_url == "/" || _url == "/index.html")
        {
            _file_path += "/";
            _file_path += homepage;
        }
        else _file_path += _url;

        auto pos = _file_path.rfind(".");
        if (pos == std::string::npos)
            suffix = ".html";
        else
            suffix = _file_path.substr(pos);

    }

    void DebugPrint()
    {
        for (auto& line : _lines)
        {
            std::cout << line << std::endl;
            std::cout << "----------------------------" << std::endl;
        }
        std::cout << _text << std::endl;

        std::cout << "method: " << _method << std::endl;
        std::cout << "url: " << _url << std::endl;
        std::cout << "httpversion: " << _httpversion << std::endl;

        std::cout << "file_path: " << _file_path << std::endl << std::endl;
    }

    ~HttpRequest() {}
public:
    std::vector<std::string> _lines;
    std::string _text;

    // 解析后结果
    std::string _method;
    std::string _url;
    std::string _httpversion;
    std::string _file_path;

    // 后缀
    std::string suffix;
};


class HttpServer
{
public:
    HttpServer(const uint16_t& port) :_port(port)
    {
        _content_type.insert({ ".html", "text/html" });
        _content_type.insert({ ".jgp", "image/jpeg" });
    }

    void Start()
    {
        _listensock.Initsocket();
        _listensock.Bind(_port);
        _listensock.Listen();

        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listensock.Accept(&clientip, &clientport);
            lg(Info, "get a link, clientip: %s, client: %d", clientip.c_str(), clientport);
            pthread_t tid;
            ThreadData* td = new ThreadData(sockfd, this);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

    static std::string readHtmlContent(const std::string& htmlpath)
    {
        std::ifstream in(htmlpath, std::ios::binary);
        if (!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);
        in.read((char*)content.c_str(), content.size());


        // std::string content;
        // std::string line;
        // while (getline(in, line))
        // {
        //     content += line;
        // }
        in.close();

        return content;
    }

    std::string suffixTodect(std::string& suffix)
    {
        auto iter = _content_type.find(suffix);
        if (iter == _content_type.end()) return _content_type[".html"];
        return _content_type[suffix.c_str()];
    }

    void HanderHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer; // 假定读到了完整的报文

            HttpRequest httpreq;
            std::string req = buffer;
            httpreq.Deserialization(req);
            httpreq.parse();
            httpreq.DebugPrint();


            // 构建一个报文
            // std::string content = "<h1>hello world</h1>";
            // std::string content = readHtmlContent("./wwwroot/index.html");
            bool ok = true;
            std::string content;
            content = readHtmlContent(httpreq._file_path);
            if (content.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                content = readHtmlContent(err_html);
            }
            std::string resp_line;
            if (ok == false)
            {
                resp_line += "HTTP/1.1 404 Not Found";
            }
            else
            {
                resp_line += "HTTP/1.1 200 ok";
            }

            // resp_line = "HTTP/1.1 302 Found";
            resp_line += "\r\n";
            std::string resp_header = "content-length: ";
            resp_header += std::to_string(content.size());
            resp_header += "\r\n";
            resp_header += "content-type: ";
            resp_header += suffixTodect(httpreq.suffix);
            resp_header += "\r\n";

            resp_header += "set-cookie: name=hhb&passwd=123456";
            resp_header += "\r\n";



            // resp_header += "Location: https://www.qq.com";
            // resp_header + "\r\n";

            std::string response;
            response += resp_line;
            response += resp_header;
            response += "\r\n";
            response += content;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }
    static void* ThreadRun(void* args)
    {
        ThreadData* td = static_cast<ThreadData*>(args);
        pthread_detach(pthread_self());

        td->svr->HanderHttp(td->sockfd);

        delete td;

        return nullptr;
    }
    ~HttpServer() {}
private:
    Socket _listensock;
    uint16_t _port;
    std::unordered_map<std::string, std::string> _content_type;
};